package miggy.memory;

import miggy.SystemModel;
import miggy.api.config.ConfigurationException;
import miggy.api.config.MachineDefinition;
import miggy.api.cpu.Size;
import miggy.api.memory.AddressSpace;
import miggy.api.memory.CustomRegisterController;
import miggy.api.memory.MappedSpace;

import java.util.ArrayList;
import java.util.logging.Logger;

/*
//  Miggy - Java Amiga MachineCore
//  Copyright (c) 2008, Tony Headford
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
//  following conditions are met:
//
//    o  Redistributions of source code must retain the above copyright notice, this list of conditions and the
//       following disclaimer.
//    o  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
//       following disclaimer in the documentation and/or other materials provided with the distribution.
//    o  Neither the name of the Miggy Project nor the names of its contributors may be used to endorse or promote
//       products derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// $Revision: 21 $
*/

public final class MemoryController implements AddressSpace
{
	private static Logger logger = Logger.getLogger("Miggy.Memory");

	private final AddressSpace[] memoryMap;
	private final ArrayList<AddressSpace> controllers;

	private MappedSpace rom;
	private MappedSpace chip;
	private MappedSpace cia;
	private boolean overlay_active;

	private int readAddress;
	private int writeAddress;

	public MemoryController()
	{
		controllers = new ArrayList<AddressSpace>();
		memoryMap = new AddressSpace[0x1000];
		overlay_active = false;
	}

	public void init(MachineDefinition config) throws ConfigurationException
	{
		AddressSpace rmc = new ReservedMemController();
		rmc.init(config);
		controllers.add(rmc);

		for(int n = 0; n < 0x1000; n++)
		{
			memoryMap[n] = rmc;
		}

		chip = new ChipMemController();
		chip.init(config);
		controllers.add(chip);

		int size = chip.getSize();
		//convert size in KB to 4KB blocks
		size /= 4;

		for(int n = 0; n < size; n++)
		{
			memoryMap[n] = chip;
		}

		AddressSpace autoconf = new AutoConfigController();
		autoconf.init(config);
		controllers.add(autoconf);

		//map autoconfig controller into 8MB of space
		for(int n = 0x0200; n < 0x0a00; n++)
		{
			memoryMap[n] = autoconf;
		}

		//CIA chips
		cia = new CIAController();
		cia.init(config);
		controllers.add(cia);
		memoryMap[0x0bfd] = cia;
		memoryMap[0x0bfe] = cia;

		//Grab The Custom Regs
		CustomRegisterController regs = SystemModel.REGS;
		controllers.add(regs);

		//SLOW RAM
		int slowsize = config.getSlowRamSize();
		if(slowsize > 0)
		{
			MappedSpace slow = new SlowMemController(0x00c00000);
			slow.init(config);
			controllers.add(slow);
			size = slow.getSize();

			//convert to 4KB blocks
			size /= 4;

			for(int n = 0x0c00; n < 0x0c00 + size; n++)
				memoryMap[n] = slow;

		}
		else
		{
			for(int n = 0x0c00; n < 0x0d80; n++)
				memoryMap[n] = new MemoryMirror(n << 12, regs);
		}

		for(int n = 0x0d80; n < 0x0dff; n++)
			memoryMap[n] = new MemoryMirror(n << 12, regs);

		// Real Time Clock (if present)
		boolean rtc = config.hasRealTimeClock();
		if(rtc)
		{
			MappedSpace clock = new RTCController();
			clock.init(config);
			controllers.add(clock);
			memoryMap[0x0dc0] = clock;
		}

		memoryMap[0x0dff] = regs;


		//do we need a different class for this region ?
		for(int n = 0x0e00; n < 0x0e90; n++)
			memoryMap[n] = autoconf;

		rom = new RomController();
		rom.init(config);
		controllers.add(rom);

		int romStart = rom.getBaseAddress() >> 12;

		for(int n = romStart; n < 0x1000; n++)
			memoryMap[n] = rom;

		reset();
	}

	public final void reset()
	{
		//reset memory and ROM
		for(AddressSpace mc: controllers)
		{
			mc.reset();
		}

		readAddress = writeAddress = 0;

		//copy ROM into start of memory
		romOverlay(true);

/*
		ChipMemController chip = (ChipMemController)controllerMap.get(CHIPRAM_NAME);
		RomController rom = (RomController)controllerMap.get(ROM_NAME);

		chip.buffer.putInt(0, rom.buffer.getInt(0));
		chip.buffer.putInt(4, rom.buffer.getInt(4));

//		chip.buffer.put(rom.buffer);
//		chip.buffer.rewind();
//		rom.buffer.rewind();
*/
	}

	private void romOverlay(boolean enable)
	{
		if(enable)
		{
			if(!overlay_active)
			{
				int size = rom.getSize();
				size /= 4;	//4K blocks
				AddressSpace mirror = new MemoryMirror(0, rom);

				for(int n = 0; n < size; n++)
				{
					//overlay ROM onto chip ram
					memoryMap[n] = mirror;
				}

				//put in special cia overlay handler
				AddressSpace cia_overlay = new CIAOverlay();
				memoryMap[0x0bfd] = cia_overlay;
				memoryMap[0x0bfe] = cia_overlay;

				overlay_active = true;
			}
		}
		else
		{
			if(overlay_active)
			{
				//remap chip ram
				int size = chip.getSize();
				//convert size in KB to 4KB blocks
				size /= 4;

				for(int n = 0; n < size; n++)
				{
					memoryMap[n] = chip;
				}

				//replace cia handler
				memoryMap[0x0bfd] = cia;
				memoryMap[0x0bfe] = cia;

				overlay_active = false;
			}
		}
	}


	public final int peek(int address, Size size)
	{
		address &= 0x00ffffff;	//24-bit addresses
		readAddress = address;
		return memoryMap[address >> 12].peek(address, size);
	}

	public final void poke(int address, int value, Size size)
	{
		address &= 0x00ffffff;	//24-bit addresses
		writeAddress = address;
		memoryMap[address >> 12].poke(address, value, size);
	}

	public final int directPeek(int address, Size size)
	{
		address &= 0x00ffffff;	//24-bit addresses
		return memoryMap[address >> 12].directPeek(address, size);
	}

	public final void directPoke(int address, int value, Size size)
	{
		address &= 0x00ffffff;	//24-bit addresses
		memoryMap[address >> 12].directPoke(address, value, size);
	}

	private class CIAOverlay implements AddressSpace
	{
		public void init(MachineDefinition config) throws ConfigurationException
		{
			//nothing to do
		}

		public void reset()
		{
			//nothing to do
		}

		public int peek(int address, Size size)
		{
			romOverlay(false);
			return cia.peek(address, size);
		}

		public void poke(int address, int value, Size size)
		{
			romOverlay(false);
			cia.poke(address, value, size);
		}

		public int directPeek(int address, Size size)
		{
			return cia.peek(address, size);
		}

		public void directPoke(int address, int value, Size size)
		{
			cia.poke(address, value, size);
		}
	}
}
